package qweather

import (
	"bytes"
	"compress/gzip"
	"context"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"github.com/valyala/fasthttp"
	"io/ioutil"
	"net/http"
	"time"
)

var reqClient *fasthttp.Client

func init() {
	reqClient = &fasthttp.Client{
		// 读超时时间,不设置read超时,可能会造成连接复用失效

		ReadTimeout: time.Second * 10,
		// 写超时时间
		WriteTimeout: time.Second * 10,
		// 10秒后，关闭空闲的活动连接
		MaxIdleConnDuration: time.Second * 10,
		Dial: (&fasthttp.TCPDialer{
			// 最大并发数，0表示无限制
			Concurrency: 4096,
		}).Dial,
	}
}

type Client struct {
	SecretKey string
}

type Weather struct {
	Temp     string `json:"temp"`
	Icon     string `json:"icon"`
	Humidity string `json:"humidity"`
}

type WeatherForecast struct {
	FxDate    string `json:"fxDate"`
	TempMax   string `json:"tempMax"`
	TempMin   string `json:"tempMin"`
	IconDay   string `json:"iconDay"`
	IconNight string `json:"iconNight"`
	Humidity  string `json:"humidity"`
}

func NewClient() *Client {
	return &Client{
		SecretKey: "21d932a1b52a4f07aaa86458a148f392",
	}
}

func (c Client) RequestNow(ctx context.Context, location string) (*Weather, error) {
	req, resp := fasthttp.AcquireRequest(), fasthttp.AcquireResponse()
	defer func() {
		fasthttp.ReleaseRequest(req)
		fasthttp.ReleaseResponse(resp)
	}()
	path := "https://api.qweather.com/v7/weather/now"
	uri := fmt.Sprintf("%s?key=%s&location=%s", path, c.SecretKey, location)
	req.SetRequestURI(uri)
	if err := reqClient.Do(req, resp); err != nil {
		return nil, errors.Wrap(err, "request now weather failure")
	}
	if resp.StatusCode() != http.StatusOK {
		return nil, errors.Errorf("request now weather response code: %d", resp.StatusCode())
	}
	response := &struct {
		Code string  `json:"code"`
		Now  Weather `json:"now"`
	}{}
	body, err := decodeGzip(resp.Body())
	if err != nil {
		return nil, errors.Wrap(err, "decode gzip failure")
	}
	if err := json.Unmarshal(body, response); err != nil {
		return nil, errors.Wrap(err, "request now weather response unmarshal failure")
	}
	if response.Code != "200" {
		return nil, errors.Errorf("request now weather response error: %v", response)
	}
	return &response.Now, nil
}

func (c Client) RequestForecast(ctx context.Context, location string) ([]WeatherForecast, error) {
	req, resp := fasthttp.AcquireRequest(), fasthttp.AcquireResponse()
	defer func() {
		fasthttp.ReleaseRequest(req)
		fasthttp.ReleaseResponse(resp)
	}()
	path := "https://api.qweather.com/v7/weather/7d"
	uri := fmt.Sprintf("%s?key=%s&location=%s", path, c.SecretKey, location)
	req.SetRequestURI(uri)
	if err := reqClient.Do(req, resp); err != nil {
		return nil, errors.Wrap(err, "request forecast weather failure")
	}
	if resp.StatusCode() != http.StatusOK {
		return nil, errors.Errorf("request forecast weather response code: %d", resp.StatusCode())
	}
	response := &struct {
		Code  string            `json:"code"`
		Daily []WeatherForecast `json:"daily"`
	}{}
	body, err := decodeGzip(resp.Body())
	if err != nil {
		return nil, errors.Wrap(err, "decode gzip failure")
	}
	if err := json.Unmarshal(body, response); err != nil {
		return nil, errors.Wrap(err, "request forecast weather response unmarshal failure")
	}
	if response.Code != "200" {
		return nil, errors.Errorf("request forecast weather response error: %v", response)
	}
	return response.Daily, nil
}

func decodeGzip(in []byte) ([]byte, error) {
	reader, err := gzip.NewReader(bytes.NewReader(in))
	if err != nil {
		var out []byte
		return out, err
	}
	defer reader.Close()
	return ioutil.ReadAll(reader)
}
